Last execution time: 24/02/2025 05:44:27
Get data
Products type filter
explore_types = ['frutas', 'lacteos', 'verduras', 'embutidos', 'panaderia', 'desayuno', 'congelados', 'abarrotes',
'aves', 'carnes', 'pescados']Data table
path = Path('../../output')
csv_files = L(path.glob('*.csv')).filter(lambda o: os.stat(o).st_size>0)
pat_store = re.compile('(.+)\_\d+')
pat_date = re.compile('.+\_(\d+)')
df = (
pd.concat([pd.read_csv(o).assign(store=pat_store.match(o.stem)[1], date=pat_date.match(o.stem)[1])
for o in csv_files], ignore_index=True)
.pipe(lambda d: d.assign(
name=d.name.str.lower()+' ('+d.store+')',
sku=d.id.where(d.sku.isna(), d.sku).astype(int),
date=pd.to_datetime(d.date)
))
.drop('id', axis=1)
.loc[lambda d: d.category.str.contains('|'.join(explore_types))]
# Filter products with recent data
# .loc[lambda d: d.name.isin(d.groupby('name').date.max().loc[ge(datetime.now()-timedelta(days=30))].index)]
# Filter empty prices
.loc[lambda d: d.price>0]
)
print(df.shape)
df.sample(3)(1272671, 8)
| sku | name | brand | category | uri | price | store | date | |
|---|---|---|---|---|---|---|---|---|
| 2205596 | 50090 | ají amarillo molido piki doypack 1kg (plaza_vea) | PIKI | https://www.plazavea.com.pe/abarrotes | https://www.plazavea.com.pe/aji-amarillo-molid... | 20.40 | plaza_vea | 2023-01-23 |
| 1234082 | 28404 | filete de salmón fresco importado (plaza_vea) | PLAZA VEA | https://www.plazavea.com.pe/carnes-aves-y-pesc... | https://www.plazavea.com.pe/filete-de-salmon-f... | 78.90 | plaza_vea | 2023-09-02 |
| 518887 | 81715 | pack b&d ketchup frasco 245g + mostaza frasco ... | B&D | https://www.plazavea.com.pe/abarrotes | https://www.plazavea.com.pe/pack-b-d-ketchup-f... | 6.99 | plaza_vea | 2023-02-23 |
Top changes (ratio)
Code
top_changes = (df
# Use last 30 days of data to compare prices
.loc[lambda d: d.date>=(datetime.now()-timedelta(days=30))]
.sort_values('date')
# Get percentage change
.assign(change=lambda d: d
.groupby(['store','sku'], as_index=False)
.price.transform(lambda d: (d-d.shift())/d.shift())
)
.groupby(['store','sku'], as_index=False)
.agg({'price':'last', 'change':'mean', 'date':'last'})
.rename({'price':'last_price', 'date':'last_date'}, axis=1)
.dropna()
.loc[lambda d: d.last_date==d.last_date.max()]
.loc[lambda d: d.change.abs().sort_values(ascending=False).index]
)
top_changes.head(3)| store | sku | last_price | change | last_date | |
|---|---|---|---|---|---|
| 5623 | plaza_vea | 11577779 | 8.3 | 0.327381 | 2025-02-24 |
| 5625 | plaza_vea | 11577781 | 8.3 | 0.327381 | 2025-02-24 |
| 5626 | plaza_vea | 11577782 | 8.3 | 0.327381 | 2025-02-24 |
Code
def plot_changes(df_changes, title):
selection = alt.selection_point(fields=['name'], bind='legend')
dff = df_changes.drop('change', axis=1).merge(df, on=['store','sku'])
return (dff
.pipe(alt.Chart)
.mark_line(point=True)
.encode(
x='date',
y='price',
color=alt.Color('name').scale(domain=sorted(dff.name.unique().tolist())),
tooltip=['name','price','last_price']
)
.add_params(selection)
.transform_filter(selection)
.interactive()
.properties(width=650, title=title)
.configure_legend(orient='top', columns=3)
)Code
top_changes.head(10).pipe(plot_changes, 'Top changes')Code
(top_changes
.sort_values('change')
.head(10)
.pipe(plot_changes, 'Top drops')
)Code
(top_changes
.sort_values('change')
.tail(10)
.pipe(plot_changes, 'Top increases')
)Top changes (absolute values)
Code
top_changes_abs = (df
# Use last 30 days of data to compare prices
.loc[lambda d: d.date>=(datetime.now()-timedelta(days=30))]
.sort_values('date')
# Get percentage change
.assign(change=lambda d: d
.groupby(['store','sku'], as_index=False)
.price.transform(lambda d: (d-d.shift()).iloc[-1])
)
.groupby(['store','sku'], as_index=False)
.agg({'price':'last', 'change':'mean', 'date':'last'})
.rename({'price':'last_price', 'date':'last_date'}, axis=1)
.dropna()
.loc[lambda d: d.last_date==d.last_date.max()]
.loc[lambda d: d.change.abs().sort_values(ascending=False).index]
)
top_changes_abs.head(3)| store | sku | last_price | change | last_date | |
|---|---|---|---|---|---|
| 2979 | plaza_vea | 10151253 | 40.9 | 18.4 | 2025-02-24 |
| 4243 | plaza_vea | 11063723 | 48.9 | 13.0 | 2025-02-24 |
| 1149 | plaza_vea | 9770 | 60.9 | 12.2 | 2025-02-24 |
Code
top_changes_abs.head(10).pipe(plot_changes, 'Top changes')Code
(top_changes_abs
.sort_values('change')
.head(10)
.pipe(plot_changes, 'Top drops')
)Code
(top_changes_abs
.sort_values('change')
.tail(10)
.pipe(plot_changes, 'Top increases')
)Search specific products
Code
(df
.loc[df.name.isin(names)]
.pipe(alt.Chart)
.mark_line(point=True)
.encode(x='date', y='price', color='name', tooltip=['name','price'])
.properties(width=650, title='Pollo')
.interactive()
.configure_legend(orient='top', columns=3)
)Code
(df
.loc[df.name.isin(names)]
.pipe(alt.Chart)
.mark_line(point=True)
.encode(x='date', y='price', color='name', tooltip=['name','price'])
.properties(width=650, title='Palta')
.interactive()
.configure_legend(orient='top', columns=3)
)Code
(df
.loc[df.name.isin(names)]
.pipe(alt.Chart)
.mark_line(point=True)
.encode(x='date', y='price', color='name', tooltip=['name','price'])
.properties(width=650, title='Aceite')
.interactive()
.configure_legend(orient='top', columns=3)
)Code
(df
.loc[df.name.isin(names)]
.pipe(alt.Chart)
.mark_line(point=True)
.encode(x='date', y='price', color='name', tooltip=['name','price'])
.properties(width=650, title='Aceite')
.interactive()
.configure_legend(orient='top', columns=3)
)Code
(df
.loc[df.name.isin(names)]
.pipe(alt.Chart)
.mark_line(point=True)
.encode(x='date', y='price', color='name', tooltip=['name','price'])
.properties(width=650, title='Aceite')
.interactive()
.configure_legend(orient='top', columns=3)
)